from Vector2D import Vector2D

################################################################################
class BaseEntity():
    """
    Virtual Base class for all game entities based on code by Matt Buckland 
        (fup@ai-junkie.com)
    """
    
    nEntityCounter = 0
    
    ############################################################################
    def __init__(self, oPosition, oScale, nRadius, nType=None, bTag=False):
        """
        param - oPosition : Vector representing Entity's initial position
        param - oScale : Vector representing Entity's initial scale
        param - nRadius : Entity's Bounding Radius
        """
        self._nId = ++self.nEntityCounter
        self._oPosition = oPosition
        self._oScale = oScale
        self._nType = nType
        self._bTag = bTag
        self._nBoundingRadius = nRadius
    ############################################################################
    
    ############################################################################
    # GetsNSets
    #
    def ID(self): return self._nId
    
    def Pos(self): return self._oPosition
    def SetPos(self, oPos): self._oPosition = oPos
    
    def BRadius(self): return self._nBoundingRadius
    def SetBRadius(self, nBRadius): self._oPosition = nBRadius
    
    def GetTagged(self): return self._bTag
    def Tag(self): self._bTag = True
    def UnTag(self): self._bTag = False
    
    def EntityType(self): return self._nType
    def SetEntityType(self, nEntType): self._nType = nEntType
    
    def Scale(self): return self._oScale
    def SetScale(self, scale):
        
        nSecondValue = self._oScale.X()
        if self._oScale.Y() > nSecondValue : nSecondValue = self._oScale.Y()
        
        nFirstValue = 0
        if isinstance(scale, Vector2D):
            nFirstValue = scale.X()
            if scale.Y() > nFirstValue : nFirstValue = scale.Y()
            self._oScale = scale
        else:
            nFirstValue = scale
            self._oScale = Vector2D(scale, scale)
            
        self.SetBRadius(nFirstValue/nSecondValue)
    ############################################################################
################################################################################

################################################################################
class MovingEntity(BaseEntity):
    """
    Virtual Base class for all moving game entities
        based on code by Matt Buckland (fup@ai-junkie.com)
    """
    ############################################################################
    def __init__(self, oPosition, oScale, nRadius, oVelocity, nMaxSpeed, 
                 oHeading, nMass, nTurnRate, nMaxForce):
        BaseEntity.__init__(self, oPosition, oScale, nRadius)
        """
        param - oPosition : Vector representing Entity's initial position
        param - oScale : Vector representing Entity's initial scale
        param - nRadius : Entity's Bounding Radius
        param - oVelocity : Vector representing Entity's initial Velocity
        param - nMaxSpeed : Entity's maximum speed
        param - oHeading : Vector representing Entity's initial heading
        param - nMass : Entity's mass
        param - nTurnRate : Entity's maximum turn rate (radians per second)
        param - nMaxForce : Entity's maximum force (as in thrust)
        """
        
        self._oVelocity = Vector2D()
        self._oHeading = Vector2D()
        self._oSide = Vector2D()
        self._nMass = float(nMass)
        self._nMaxSpeed = float(nMaxSpeed)
        self._nMaxForce = float(nMaxForce)
        self._nMaxTurnRate = float(nTurnRate)
    ############################################################################
    
    ############################################################################
    # GetsNSets
    #
    def Velocity(self): return self._oVelocity
    def SetVelocity(self, oNewVelocity): self._oVelocity = oNewVelocity
    def Heading(self): return self._oHeading
    def SetHeading(self, oNewHeading): 
        self._oHeading = oNewHeading
        assert( (oNewHeading.LengthSq() -1.0) < 0.00001 )
        
        self._oHeading = oNewHeading
        self._oSide = self._oHeading.Perp()
        
    def Side(self): return self._oSide
    
    def Mass(self): return self._nMass
    
    def MaxSpeed(self): return self._nMaxSpeed
    def SetMaxSpeed(self, nNewMaxSpeed): self._nMaxSpeed = nNewMaxSpeed
    
    def MaxForce(self): return self._nMaxForce
    def SetMaxForce(self, nNewMaxForce): self._nMaxForce = nNewMaxForce
    
    def Speed(self): return self._oVelocity.Length()
    def SpeedSq(self): return self._oVelocity.LengthSq()
    def IsSpeedMaxedOut(self): return (self.MaxSpeed() * self.MaxSpeed()) >= (self.Velocity().LengthSq())
    
    def MaxTurnRate(self): return self._nMaxTurnRate
    def SetMaxTurnRate(self, nNewMaxTurnRate): self._nMaxTurnRate = nNewMaxTurnRate
    
    ############################################################################ 
    
    ############################################################################
    def RotateHeadingToFacePosition(self):
        """
        Given a target posiion, the entitiy will be rotated towards the target 
            by an amount not greater than its MaxTurnRate
            
        param - oTargetHeading : Vector to turn towards
        
        return - bFacingTarget : True if entity now faces TargetHeading
        """
        # TODO: This method!
        pass
    ############################################################################
    
    ############################################################################
    def ApplyForce(self, oForce, nTime):
        """
        Apply a force to the entitiy for a specified time period
        
        param - oForce : A vector representing the force applied to the entity
        param - nTime : Length of time to apply the force
        """
        
        # Acceleration = Force/Mass
        oAcceleration = oForce / self._nMass
        
        # update velocity
        self._oVelocity += oAcceleration * nTime 
        
        # make sure vehicle does not exceed maximum velocity
        self._oVelocity.Truncate(self._nMaxSpeed);
        
        # update the position
        self._oPosition += self._oVelocity * nTime
        
        # update the heading if the entity has a non zero velocity
        if (self._oVelocity.LengthSq() > 0.00000001):
            import copy
            self._oHeading = copy.deepcopy(self._oVelocity)
            self._oHeading.Normalize()
            self._oSide = self._oHeading.Perp()
    ############################################################################
################################################################################
        